/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package biofilter.records;

import java.util.*;
import biofilter.util.*;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.IllegalArgumentException;

/**
 * An HistogramRecord is a bit like a histogram;
 * On the X axis is the coordinates along the genome from position 1 to N where N is the length of the genome or plasmid
 * On the Y axis is the coordinates corresponding to the summed values from probes (perhaps normalized) or reads from a sequencer.
 * The function of this class is to ADD values given by the record.weight() function, not to pre-treat the data.
 * @author Daniel Quest
 * @date   Dec 22 2009
 */
public class HistogramRecord implements Record {

    private String record = null;
    private HashMap positiveStrand = null;
    private HashMap negativeStrand = null;
    private int greatestIndex;
    public HistogramRecord(){
        this.record = null;
        positiveStrand = new HashMap();
        negativeStrand = new HashMap();
        greatestIndex = 7;  //not realisitic, but it will grow to needed size
        this.readsAdded = 0;
        this.bpAdded = 0;
        map(1, 2, '+', 0.0);
    }

    /**
     * Note:  It is a really bad idea to use get and set in this class as these objects could have ~10 M of data.
     * Doing a get or a set on this size of data source will DUPLICATE the record!
     * @param s
     */
    public void set(String s){
        record = s;
    }

    public String get(){
        if (this.record == null){ createRecord(); }
        return record;
    }

    /**
     * setMapMax is set, all reads over MapMax will not be mapped
     * @return
     */
    private int mapmax = Integer.MIN_VALUE;
    public void setMapMax(int mpmx){
        this.mapmax = mpmx;
    }
    
    public int getReadsAddedToHistogram(){
        return this.readsAdded;
    }

    public int getBPAdded() {
        return bpAdded;
    }


    public void toFile(String fileNamePath){
        StringBuffer sb = new StringBuffer("");
        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(fileNamePath));
            out.write(this.createRecord());
            out.close();
        } catch (IOException e) { }
    }

//    public String createRecord(){
//        record = "";
//        String r = "";
//        StringBuffer sb = new StringBuffer("");
//        Set ids = (Set) this.positiveStrand.keySet();
//        Iterator i = ids.iterator();
//        while(i.hasNext()){
//            r = createRecord( (String) i.next() );
//            sb.append(r);
//        }
//        record = sb.substring(0);
//        return record;
//    }
    public String createRecord(){
        StringBuffer sb = new StringBuffer("");
        Double p = new Double("0");
        Double n = new Double("0");
        for(int i=1; i<=greatestIndex;i++){
            if(this.positiveStrand.containsKey(i) == false){
                p = 0.0;
            }else {
                p = (Double) this.positiveStrand.get(i);
            }
            if (this.negativeStrand.containsKey(i) == false ){
                n = 0.0;
            }else {
                n = (Double) this.negativeStrand.get(i);
            }
            sb.append(p.toString() + " " + n.toString() + "\n");
            //System.err.println(sb);
        }
        this.record = sb.substring(0);
        return this.record;
    }

    /**
     * Map adds the MappedPosition, mp, to the Histogram record.
     * For example if mp contains(2,7,+,2),
     * and the positive histogram record looked as follows before the positon is mapped:
     *                   1                   2
     * 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 - position
     * 0 0 0 0 0 1 1 1 1 2 2 2 2 2 1 1 1 1 1 0 0 0 0 0 - value
     *
     * map will create (notice the change from positons 2-7):
     *                   1                   2
     * 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 - position
     * 0 2 2 2 2 3 3 1 1 2 2 2 2 2 1 1 1 1 1 0 0 0 0 0 - value
     *
     * @param mp
     * @throws IllegalArgumentException
     */
    public void map(int start, int end, char strand, double weight) throws IllegalArgumentException {
        this.record = null;
        if(this.greatestIndex < start ){
            this.greatestIndex = start;
        }
        if(this.greatestIndex < end){
            this.greatestIndex = end;
        }
        if(strand == '+'){
            incrementCount(start, end, this.positiveStrand, weight);
            //System.err.println( this.positiveStrand.toString() );
        }else if(strand == '-'){
            incrementCount(start, end, this.negativeStrand, weight);
        }else {
            throw new IllegalArgumentException("Undefined Strand! " + strand);
        }
    }
    public void map(SequencePosition mp) throws IllegalArgumentException {
        map(mp.getStart(), mp.getEnd(), mp.getStrand(), mp.getWeight());
    }

    private int readsAdded = 0; //Reads for RNA-Seq; probes for tiling arrays...
    private int bpAdded = 0;    //note bp added is a count (regardless of strand) for the total number of positions added
    //For example consider the following instructions:
    //add(1,+)
    //add(2,+)
    //add(1,-)
    //bp added will return 3.  Even if the values added under each instruction differ, the result will still be the number of
    //addition operations!

    /**
     * Add a weight to all values in the histogram over a range.
     * e.g. add 3 to positions 3-7
     * @param start
     * @param end
     * @param hm
     * @param weight
     */
    private void incrementCount(int start, int end, HashMap hm, double weight){
        this.readsAdded++;
        double tmp = 0;
        int temp = 0;
        if (end < start){
            temp = end;
            end = start;
            start = temp;
        }
        for(int i=start; i<=end;i++){
            this.bpAdded++;
            if ( hm.containsKey(i) == true){
                tmp = (Double) hm.get(i);
            }else {
                tmp = 0.0;
            }
            double next;
            if (this.mapmax > Integer.MIN_VALUE){
                if (tmp >= this.mapmax) {
                    next = this.mapmax;
                }else {
                    next = tmp + weight;
                }
            } else {
                next = tmp + weight;
            }
            //System.err.println(i + ":" + tmp +"->" + next);
            hm.put(i, next);
        }
        return;
    }


    /**
     * Calculates a weight to all values in the histogram over a range.
     * e.g. sum values from position 3 until 7 +
     * @param start position to start summing values
     * @param end   position to end summing values
     * @param strand +/-/. . represents both strands
     */
     public double scoreRegion(int start, int end, char strand){
         double total = 0.0;
         for(int i=start; i<=end;i++){
            if(strand == '+' || strand == '.'){
                total = total + (Double) this.positiveStrand.get(i);
            }
            if(strand == '-' || strand == '.'){
                total = total + (Double) this.negativeStrand.get(i);
            }           
         }
         return total;
     }

     public double getPosition(int position, char strand){
         //System.out.println(position);
         if( this.positiveStrand.containsKey(position) ){
            if(strand == '+'){
                return (Double) this.positiveStrand.get(position);
            }
         }
         if( this.negativeStrand.containsKey(position) ){
             if(strand == '-'){
                 return (Double) this.negativeStrand.get(position);
             }
         }
         //both strands
         if ( this.positiveStrand.containsKey(position) && this.negativeStrand.containsKey(position) ){
             if(strand == '.'){
                 return (Double) this.positiveStrand.get(position) + (Double) this.negativeStrand.get(position);
             }             
         }
         return 0.0;
     }

    /**
     * TargetDNA is a genome, plasmid, or contig that reads are mapped onto.
     * A collection of HistogramRecords can have more than one target.  In this class,
     * it is assumed that the subject/database of a mapper (e.g. bowtie or blast)
     * will contain S id's representing each of the subjects in the database.
     * For example if the mapping is done against 1 genome and two plasmids, S=3
     * and there will be one source for each of the id's
     *
     * Also the id is assumed to be MORE complete than whatever abbreviation comes out
     * of the program.  So id can be: ctg95  [organism=Burkholderia cenocepacia AU 1054] [tech=wgs] [gcode=11] [strain=AU 1054]
     * and the program can return: ctg95
     * in this case things will work as long as ctg95 is unique in the id's provided.
     * However the ID could NOT be ctg!
     */
    private String target = "";
    public void setTarget(String id){
        this.target = id.trim();
    }
    
    public String getTarget(){
        return this.target;
    }

    public boolean targetMatches(String id){
        String tmp = id.trim();
        if (target.equalsIgnoreCase("") == true){ return false; }
        return this.target.startsWith(id);
    }


}
